package cn.innosmart.aq.util;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import cn.innosmart.aq.R;

/**
 * Created by David on 2015/10/16.
 */
public class CrashHandler implements Thread.UncaughtExceptionHandler {
    private static final String TAG = "CrashHandler";
    private static final String CRASH_FLOD_NAME = "crash";

    //系统默认的UncaughtException处理类
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    //CrashHandler实例
    private static CrashHandler INSTANCE = new CrashHandler();
    //程序的Context对象
    private Context mContext;

    //用于格式化日期,作为日志文件名的一部分
    private DateFormat formatter = new SimpleDateFormat("yyyyMMdd_kkmmss");

    /** 保证只有一个CrashHandler实例 */
    private CrashHandler() {
    }

    /** 获取CrashHandler实例 ,单例模式 */
    public static CrashHandler getInstance() {
        return INSTANCE;
    }

    /**
     * 初始化
     *
     * @param context
     */
    public void init(Context context) {
        mContext = context;
        //获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        //设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            //如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                Log.e(TAG, "error : ", e);
            }
            //退出程序
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
        }
    }

    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }

        //异步工作：获取版本信息、写入文件、发送邮件
        ProgressTask mTask = new ProgressTask(mContext);
        mTask.execute(ex);


        //使用Toast来显示异常信息
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext, R.string.crash_info, Toast.LENGTH_LONG).show();
                Looper.loop();
            }
        }.start();

        return true;
    }


    private class ProgressTask extends AsyncTask<Throwable, Void, Boolean> {
        private Context mContext;

        public ProgressTask(Context vContext){
            mContext = vContext;
        }

        protected void onPreExecute() {
        }


        protected void onPostExecute(Boolean result) {
        }

        @Override
        protected Boolean doInBackground(Throwable... params) {
            boolean mResult = false;

            //保存日志文件
            String filePath = saveCrashInfo2File(mContext,params[0]);
            Log.d("test","filePath: "+filePath);

            //发送邮件
            if(filePath != null)
            {
                boolean isSuccessMail = sendMailByJavaMail(filePath);
                Log.d("test","isSuccessMail: "+isSuccessMail);

                //如果发送成功，则删除本地的crash report
                if(isSuccessMail){
                    deleteFile(filePath);
                }

                mResult = isSuccessMail;
            }

            return mResult;
        }


        /**
         * 收集设备参数信息
         * @param ctx
         */
        private Map<String, String> collectDeviceInfo(Context ctx) {
            Map<String, String> infos = new HashMap<String, String>();
            try {
                PackageManager pm = ctx.getPackageManager();
                PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
                if (pi != null) {
                    infos.put("packageName", pi.packageName);
                    String versionName = pi.versionName == null ? "null" : pi.versionName;
                    String versionCode = pi.versionCode + "";
                    infos.put("versionName", versionName);
                    infos.put("versionCode", versionCode);
                }
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "an error occured when collect package info", e);
            }
            Field[] fields = Build.class.getDeclaredFields();
            for (Field field : fields) {
                try {
                    field.setAccessible(true);
                    infos.put(field.getName(), field.get(null).toString());
                    Log.d(TAG, field.getName() + " : " + field.get(null));
                } catch (Exception e) {
                    Log.e(TAG, "an error occured when collect crash info", e);
                }
            }
            return infos;
        }

        /**
         * 保存错误信息到文件中
         *
         * @param ex
         * @return    返回文件名称,便于将文件传送到服务器
         */
        private String saveCrashInfo2File(Context ctx,Throwable ex) {
            Map<String, String> infos = collectDeviceInfo(ctx);

            StringBuffer sb = new StringBuffer();
            for (Map.Entry<String, String> entry : infos.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                sb.append(key + "=" + value + "\n");
            }

            Writer writer = new StringWriter();
            PrintWriter printWriter = new PrintWriter(writer);
            ex.printStackTrace(printWriter);
            Throwable cause = ex.getCause();
            while (cause != null) {
                cause.printStackTrace(printWriter);
                cause = cause.getCause();
            }
            printWriter.close();
            String result = writer.toString();
            sb.append(result);
            try {
                long timestamp = System.currentTimeMillis();
                String time = formatter.format(new Date());
                String fileName = "crash_" + time + "_" + timestamp + ".log";
                String fileAbsPath = "";
                if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) &&
                        !Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
                    fileAbsPath = Environment.getExternalStorageDirectory().getPath()+ File.separator+CRASH_FLOD_NAME+File.separator;
                }
                else{
                    fileAbsPath = mContext.getFilesDir().getPath()+File.separator+CRASH_FLOD_NAME+File.separator;
                }
                File dir = new File(fileAbsPath);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(fileAbsPath + fileName);
                fos.write(sb.toString().getBytes());
                fos.close();
                return fileAbsPath + fileName;
            } catch (Exception e) {
                Log.e(TAG, "an error occured while writing file...", e);
            }
            return null;
        }


        private  boolean sendMailByJavaMail(String filePath) {
//            Mail m = new Mail("xx@126.com", "xxxx");
//            m.set_debuggable(false);
//            String[] toArr = {"xx@126.com"};
//            m.set_to(toArr);
//            m.set_from("xx@126.com");
//            m.set_subject("CrashReport");
//            m.setBody("Email body. test by Java Mail final");
//            try {
//                m.addAttachment(filePath);
//
//                if(m.send()) {
//                    Log.i("test","Email was sent successfully.");
//                    return true;
//
//                } else {
//                    Log.i("test","Email was sent failed.");
//                    return false;
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//                Log.e("test", "Could not send email", e);
//            }

            return false;
        }


        private void deleteFile(String filePath)
        {
            File file = new File(filePath);
            if(file!= null && file.exists()){
                file.delete();
            }
        }
    }
}
